Next: Debugging Rewrites, Previous: Matching Commands, Up: Rewrite Rules [Contents][Index]
It is possible to get Calc to apply a set of rewrite rules on
all results, effectively adding to the built-in set of default
simplifications. To do this, simply store your rule set in the
variable EvalRules. There is a convenient s
E command for editing EvalRules; see Operations
on Variables.
For example, suppose you want ‘sin(a + b)’ to be expanded out to ‘sin(b) cos(a) + cos(b) sin(a)’ wherever it appears, and similarly for ‘cos(a + b)’. The corresponding rewrite rule set would be,
[ sin(a + b) := cos(a) sin(b) + sin(a) cos(b), cos(a + b) := cos(a) cos(b) - sin(a) sin(b) ]
To apply these manually, you could put them in a variable
called trigexp and then use a r trigexp
every time you wanted to expand trig functions. But if instead
you store them in the variable EvalRules, they will
automatically be applied to all sines and cosines of sums. Then,
with ‘2 x’ and
‘45’ on the stack, typing + S
will (assuming Degrees mode) result in ‘0.7071 sin(2
x) + 0.7071 cos(2 x)’ automatically.
As each level of a formula is evaluated, the rules from
EvalRules are applied before the default
simplifications. Rewriting continues until no further
EvalRules apply. Note that this is different from
the usual order of application of rewrite rules:
EvalRules works from the bottom up, simplifying the
arguments to a function before the function itself, while a
r applies rules from the top down.
Because the EvalRules are tried first, you can
use them to override the normal behavior of any built-in Calc
function.
It is important not to write a rule that will get into an
infinite loop. For example, the rule set ‘[f(0) := 1,
f(n) := n f(n-1)]’ appears to be a good definition
of a factorial function, but it is unsafe. Imagine what happens
if ‘f(2.5)’ is simplified. Calc will
continue to subtract 1 from this argument forever without
reaching zero. A safer second rule would be ‘f(n) :=
n f(n-1) :: n>0’. Another dangerous rule is
‘g(x, y) := g(y, x)’. Rewriting
‘g(2, 4)’, this would bounce back and
forth between that and ‘g(4, 2)’
forever. If an infinite loop in EvalRules occurs,
Emacs will eventually stop with a “Computation got stuck or
ran too long” message.
Another subtle difference between EvalRules and
regular rewrites concerns rules that rewrite a formula into an
identical formula. For example, ‘f(n) :=
f(floor(n))’ “fails to match” when
‘n’ is already an integer. But in
EvalRules this case is detected only if the
righthand side literally becomes the original formula before any
further simplification. This means that ‘f(n) :=
f(floor(n))’ will get into an infinite loop if it
occurs in EvalRules. Calc will replace
‘f(6)’ with
‘f(floor(6))’, which is different from
‘f(6)’, so it will consider the rule to
have matched and will continue simplifying that formula; first
the argument is simplified to get
‘f(6)’, then the rule matches again to
get ‘f(floor(6))’ again, ad infinitum. A
much safer rule would check its argument first, say, with
‘f(n) := f(floor(n)) :: !dint(n)’.
(What really happens is that the rewrite mechanism substitutes
the meta-variables in the righthand side of a rule, compares to
see if the result is the same as the original formula and fails
if so, then uses the default simplifications to simplify the
result and compares again (and again fails if the formula has
simplified back to its original form). The only special wrinkle
for the EvalRules is that the same rules will come
back into play when the default simplifications are used. What
Calc wants to do is build ‘f(floor(6))’,
see that this is different from the original formula, simplify to
‘f(6)’, see that this is the same as the
original formula, and thus halt the rewriting. But while
simplifying, ‘f(6)’ will again trigger
the same EvalRules rule and Calc will get into a
loop inside the rewrite mechanism itself.)
The phase, schedule, and
iterations markers do not work in
EvalRules. If the rule set is divided into phases,
only the phase 1 rules are applied, and the schedule is ignored.
The rules are always repeated as many times as possible.
The EvalRules are applied to all function calls
in a formula, but not to numbers (and other number-like objects
like error forms), nor to vectors or individual variable names.
(Though they will apply to components of vectors and
error forms when appropriate.) You might try to make a variable
phihat which automatically expands to its definition
without the need to press = by writing the rule
‘quote(phihat) := (1-sqrt(5))/2’, but
unfortunately this rule will not work as part of
EvalRules.
Finally, another limitation is that Calc sometimes calls its
built-in functions directly rather than going through the default
simplifications. When it does this, EvalRules will
not be able to override those functions. For example, when you
take the absolute value of the complex number ‘(2,
3)’, Calc computes ‘sqrt(2*2 +
3*3)’ by calling the multiplication, addition, and
square root functions directly rather than applying the default
simplifications to this formula. So an EvalRules
rule that (perversely) rewrites ‘sqrt(13) :=
6’ would not apply. (However, if you put Calc into
Symbolic mode so that ‘sqrt(13)’ will be
left in symbolic form by the built-in square root function, your
rule will be able to apply. But if the complex number were
‘(3,4)’, so that
‘sqrt(25)’ must be calculated, then
Symbolic mode will not help because
‘sqrt(25)’ can be evaluated exactly to
5.)
One subtle restriction that normally only manifests itself
with EvalRules is that while a given rewrite rule is
in the process of being checked, that same rule cannot be
recursively applied. Calc effectively removes the rule from its
rule set while checking the rule, then puts it back once the
match succeeds or fails. (The technical reason for this is that
compiled pattern programs are not reentrant.) For example,
consider the rule ‘foo(x) := x :: foo(x/2) >
0’ attempting to match
‘foo(8)’. This rule will be inactive
while the condition ‘foo(4) > 0’ is
checked, even though it might be an integral part of evaluating
that condition. Note that this is not a problem for the more
usual recursive type of rule, such as ‘foo(x) :=
foo(x/2)’, because there the rule has succeeded and
been reactivated by the time the righthand side is evaluated.
If EvalRules has no stored value (its default
state), or if anything but a vector is stored in it, then it is
ignored.
Even though Calc’s rewrite mechanism is designed to
compare rewrite rules to formulas as quickly as possible, storing
rules in EvalRules may make Calc run substantially
slower. This is particularly true of rules where the top-level
call is a commonly used function, or is not fixed. The rule
‘f(n) := n f(n-1) :: n>0’ will only
activate the rewrite mechanism for calls to the function
f, but ‘lg(n) + lg(m) := lg(n
m)’ will check every ‘+’
operator.
apply(f, [a*b]) := apply(f, [a]) + apply(f, [b]) :: in(f, [ln, log10])
may seem more “efficient” than two separate rules
for ln and log10, but actually it is
vastly less efficient because rules with apply as
the top-level pattern must be tested against every
function call that is simplified.
Suppose you want ‘sin(a + b)’ to be
expanded out not all the time, but only when algebraic
simplifications are used to simplify the formula. The variable
AlgSimpRules holds rules for this purpose. The
a s command will apply EvalRules and
AlgSimpRules to the formula, as well as all of its
built-in simplifications.
Most of the special limitations for EvalRules
don’t apply to AlgSimpRules. Calc simply does
an a r AlgSimpRules command with an infinite repeat
count as the first step of algebraic simplifications. It then
applies its own built-in simplifications throughout the formula,
and then repeats these two steps (along with applying the default
simplifications) until no further changes are
possible.
There are also ExtSimpRules and
UnitSimpRules variables that are used by a
e and u s, respectively; these commands also
apply EvalRules and AlgSimpRules. The
variable IntegSimpRules contains simplification
rules that are used only during integration by a
i.
Next: Debugging Rewrites, Previous: Matching Commands, Up: Rewrite Rules [Contents][Index]